iT邦幫忙

1

短網址使用更省力:npm 一行裝好短網址 SDK,支援批次建立 + OG 預覽 + 點擊統計

  • 分享至 

  • xImage
  •  

tl;dr:npm install toui-js,三個 method 涵蓋全部 v1 公開 API,零依賴。文末有完整可跑 script,行銷部門下次丟 50 條連結來、30 秒搞定。

起因

我在維護一個叫 toui.io 的短網址服務(名字是台語「佗位(tó-uī)」,意思是「哪裡」)。原本的 REST API 已經開放了,文件用 curl 或 fetch 範例帶過——但發現每次都要手寫 header、處理 4xx/429、把回應 type 補出來很麻煩,所以這幾天把 SDK 寫了出來,剛上 npm:

npm install toui-js
  • GitHub:https://github.com/thebrecht/toui-js
  • npm:https://www.npmjs.com/package/toui-js
  • MITzero depsESM-only、TypeScript 寫的、Node 18+ / Bun / Deno / Cloudflare Workers 都能跑

這篇就是用這個 SDK 走一輪實戰:用一個促銷活動的場景,batch 建短網址 + 加社群預覽 + 活動結束拉成效報表

開始之前

你需要:

  • 一個 toui.io 帳號(Free 方案就能用 API:20 req/min、5,000/月、500/day)
  • 一組 API Key — 登入後台 → API Keys → 建立。長相 toui_ 開頭加 32 字元 hex
  • Node.js 18+

把 API Key 放環境變數:

# .env
TOUI_API_KEY=toui_your_key_here

.env 一定要進 .gitignore。Node 20.6+ 可以用內建的 --env-file=.env 旗標跳過 dotenv。

裝套件:

npm install toui-js
# 或 pnpm add toui-js / bun add toui-js

初始化 client:

import "dotenv/config";
import { Toui } from "toui-js";

const toui = new Toui({ apiKey: process.env.TOUI_API_KEY });

就這樣,可以開工了。

Step 1:批次建立短網址

情境:行銷部門給了你一份商品清單,每條都要變短網址、要加 LINE / Facebook 分享預覽。

import "dotenv/config";
import { Toui, TouiError } from "toui-js";

const toui = new Toui({ apiKey: process.env.TOUI_API_KEY });

const products = [
  {
    url: "https://shop.example.com/airpods-pro-2",
    title: "AirPods Pro 2",
    code: "airpods",
    og_title: "AirPods Pro 2 限時特價 $5,990",
    og_description: "原價 $7,490,週末兩天限定。主動降噪、USB-C 充電盒。",
  },
  {
    url: "https://shop.example.com/air-fryer-japan",
    title: "日本氣炸鍋",
    code: "airfryer",
    og_title: "日本氣炸鍋 現折 $1,200",
    og_description: "4.5L 大容量,八種預設模式,少油更健康。",
  },
  {
    url: "https://shop.example.com/robot-vacuum-x1",
    title: "掃地機器人 X1",
    code: "vacuum",
    og_title: "掃地機器人 X1 直降 $3,000",
    og_description: "雷射導航、自動集塵、App 遠端遙控。",
  },
];

async function main() {
  console.log("Creating short URLs for flash sale...\n");

  for (const p of products) {
    try {
      const link = await toui.shorten({
        url: p.url,
        title: p.title,
        custom_code: p.code,
        og_title: p.og_title,
        og_description: p.og_description,
      });
      console.log(`${p.title}: ${link.short_url}`);
    } catch (err) {
      if (err instanceof TouiError) {
        console.error(`✗ ${p.title} (${err.status}): ${err.message}`);
      } else {
        throw err;
      }
    }
  }

  console.log("\nDone! All links ready.");
}

main();

跑起來:

Creating short URLs for flash sale...

AirPods Pro 2: https://toui.io/airpods
日本氣炸鍋: https://toui.io/airfryer
掃地機器人 X1: https://toui.io/vacuum

Done! All links ready.

幾個重點:

  • custom_code 是選填、4-8 個英數字元,不填會自動產生 6 碼隨機短碼。自訂短碼是付費功能,Free 方案會收到 403
  • og_title / og_description 也是選填,但連結要丟 LINE / Facebook 強烈建議填——預覽卡片好不好看,直接影響點擊率
  • title 是後台辨識用的內部標籤,不會對外顯示
  • 拋出來的 TouiErrorstatusmessage,分流處理比硬接 generic Error 漂亮很多

50 條商品?把 products 換成從 Excel / DB 讀進來的資料,邏輯一樣,兩分鐘跑完。

Step 2:確認連結狀態

建完想 spot-check 一下某條連結:

const info = await toui.get("airpods");

console.log("Short code:", info.short_code);
console.log("Target:", info.target_url);
console.log("Title:", info.title);
console.log("Clicks:", info.click_count);
console.log("OG title:", info.og_title);
console.log("Active:", info.is_active); // 真 boolean,SDK 已從 0/1 normalize

回傳欄位(TypeScript 的話 hover 會看到完整型別):

欄位 說明
short_code 短碼
target_url 目標網址
title 後台用標題
click_count 累計點擊數
is_active 是否啟用中(boolean)
og_title / og_description / og_image_url 社群預覽
created_at 建立時間(YYYY-MM-DD HH:MM:SS UTC)

正式環境通常不會每條都查,但寫進建立流程尾端做一次 spot check,debug 的時候會感謝自己。

Step 3:活動結束,拉成效

週末結束,行銷部門問:「哪個商品點最多?流量從哪來?」

async function report() {
  const codes = ["airpods", "airfryer", "vacuum"];

  for (const code of codes) {
    const stats = await toui.stats(code, { days: 7 });

    console.log(`\n--- ${stats.short_code} ---`);
    console.log(`Total clicks: ${stats.total_clicks}`);

    for (const day of stats.daily) {
      console.log(
        `  ${day.date}: ${day.clicks} (${day.unique_visitors} unique)`,
      );
    }

    if (stats.countries.length) {
      console.log(
        "Top countries:",
        stats.countries.map((c) => `${c.country}:${c.clicks}`).join(", "),
      );
    }
    if (stats.referers.length) {
      console.log(
        "Top referers:",
        stats.referers.map((r) => `${r.referer}:${r.clicks}`).join(", "),
      );
    }
    if (stats.devices.length) {
      console.log(
        "Devices:",
        stats.devices.map((d) => `${d.device}:${d.clicks}`).join(", "),
      );
    }
  }
}

report();

跑出來:

--- airpods ---
Total clicks: 1847
  2026-05-03: 823 (614 unique)
  2026-05-04: 1024 (789 unique)
Top countries: TW:1650, HK:102, US:53
Top referers: line.me:894, facebook.com:612, direct:341
Devices: mobile:1423, desktop:424

一眼看出:LINE 帶來的流量比 Facebook 多、絕大多數手機點。下次活動素材怎麼調,數據說了算。

注意:進階分析(countries / referers / devices / browsers 的 breakdown)需要 Pro 以上方案。Free 方案的 stats 回傳會帶 limited: true,那幾個 array 會是空的。

正式環境注意事項

速率限制速查

方案 每分鐘 每月 API 總量
Free 20 req/min 5,000
Pro 200 req/min 500,000
Business 600 req/min 500,000

超過會 429。SDK 拋 TouiErrorstatus: 429。一次建幾百條的話,加個簡單 backoff:

async function shortenWithRetry(input, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await toui.shorten(input);
    } catch (err) {
      if (err instanceof TouiError && err.status === 429) {
        const wait = 5 * attempt;
        console.log(`Rate limited, waiting ${wait}s (attempt ${attempt})`);
        await new Promise((r) => setTimeout(r, wait * 1000));
        continue;
      }
      throw err;
    }
  }
  throw new Error("Max retries exceeded");
}

錯誤碼速查

HTTP 意思 怎麼處理
400 參數錯誤 檢查 request body
401 API Key 無效 確認 Authorization header(SDK 自動帶)
403 權限不足(Free 用自訂短碼、目標網址被 Safe Browsing 擋) 確認方案或目標網址
404 短碼不存在或不屬於你的團隊 確認短碼正確
429 超速 backoff 後重試

SDK 全部統一拋 TouiErrorerr instanceof TouiError 一條就分流完。

自帶 fetch / log

SDK 接受 fetch 注入,做 logging 或代理很方便:

const toui = new Toui({
  apiKey: process.env.TOUI_API_KEY,
  fetch: async (input, init) => {
    const start = Date.now();
    const res = await fetch(input, init);
    console.log(
      `${init?.method ?? "GET"} ${input} -> ${res.status} (${Date.now() - start}ms)`,
    );
    return res;
  },
});

Cloudflare Worker 也能跑

import { Toui } from "toui-js";

export default {
  async fetch(req, env) {
    const toui = new Toui({ apiKey: env.TOUI_API_KEY });
    const { url } = await req.json();
    const link = await toui.shorten({ url });
    return Response.json(link);
  },
};

Next.js Route Handler、Bun、Deno 同理,只要 runtime 有 fetch 就能跑。

收工

回頭看,你用不到 100 行:

  1. 批次建立帶社群預覽的短網址
  2. 自動 spot-check 連結狀態
  3. 拉完整成效報表(含國家、來源、裝置)

這套包成 script,下次行銷部門丟連結來,改個 products 陣列、跑一次就搞定。

更進階——串電商後台、新品自動建短網址、定期報表寄 email——都是這個基礎再加工。

資源


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言